#include "SFSDBOperation.h"
#include "MysqlHelper.h"

#include <iostream>

using std::cout;
using std::endl;

#define LOGGER_NAME "shadow.sfs.db.sfsdboperation"

SFSDBOperation::SFSDBOperation() {
		 _logger = log4cxx::Logger::getLogger(LOGGER_NAME);
}

SFSDBOperation::~SFSDBOperation() {}

int SFSDBOperation::get_data_node_config_by_ip_and_port(MYSQL * mysql, list<SFS_DATA_NODE_CONFIG> & l_config, \
				const char * data_node_ip, const int data_node_port) {
		if (NULL == mysql || NULL == data_node_ip || strlen(data_node_ip) < 1 || data_node_port < 1) {
				LOG4CXX_ERROR(_logger, "get_data_node_config_by_ip_and_port error, invalid args");
				return -1;
		}
		string e_data_node_ip;
		MysqlHelper mysql_helper;
		int res = mysql_helper.escape_string(mysql, e_data_node_ip, data_node_ip, strlen(data_node_ip));
		if (res < 0) return -2;
		char t[1024];
		snprintf(t, sizeof(t), "%d", data_node_port);
		string sql = string("select DATA_NODE_IP, DATA_NODE_PORT, DATA_NODE_NAMESPACE_ID, CREATE_TIME from "
						"SFS_DATA_NODE_CONFIG where DATA_NODE_IP = \'") + e_data_node_ip + "\' and DATA_NODE_PORT = " + t;
		res = mysql_helper.query(mysql, sql.c_str(), sql.size());
		if (res < 0) return -3;
		MYSQL_RES * mysql_res = mysql_helper.store_result(mysql, sql.c_str());
		if (NULL == mysql_res) return -4;
		MYSQL_ROW row;
		struct tm ttm;
		while ((row = mysql_fetch_row(mysql_res)) != NULL) {
				SFS_DATA_NODE_CONFIG config;
				config.data_node_ip = row[0];
				config.data_node_port = atoi(row[1]);
				config.data_node_namespace_id = atoi(row[2]);
				strptime(row[3], "%Y-%m-%d %H:%M:%S", &ttm);
				config.create_time = mktime(&ttm);
				l_config.push_back(config);
		}
		mysql_free_result(mysql_res);
		if (l_config.size() < 1) return SFS_DBOP_NOT_FOUND;
		return 1;
}

int SFSDBOperation::get_all_data_node_config(MYSQL * mysql, list<SFS_DATA_NODE_CONFIG> & l_config) {
		if (NULL == mysql) {
				LOG4CXX_ERROR(_logger, "get_all_data_node_config error, invalid args");
				return -1;
		}
		MysqlHelper mysql_helper;
		string sql = string("select DATA_NODE_IP, DATA_NODE_PORT, DATA_NODE_NAMESPACE_ID, CREATE_TIME from "
						"SFS_DATA_NODE_CONFIG");
		int res = mysql_helper.query(mysql, sql.c_str(), sql.size());
		if (res < 0) return -2;
		MYSQL_RES * mysql_res = mysql_helper.store_result(mysql, sql.c_str());
		if (NULL == mysql_res) return -3;
		MYSQL_ROW row;
		struct tm ttm;
		while ((row = mysql_fetch_row(mysql_res)) != NULL) {
				SFS_DATA_NODE_CONFIG config;
				config.data_node_ip = row[0];
				config.data_node_port = atoi(row[1]);
				config.data_node_namespace_id = atoi(row[2]);
				strptime(row[3], "%Y-%m-%d %H:%M:%S", &ttm);
				config.create_time = mktime(&ttm);
				l_config.push_back(config);
		}
		mysql_free_result(mysql_res);
		if (l_config.size() < 1) return SFS_DBOP_NOT_FOUND;
		return 1;
}


int SFSDBOperation::get_data_node_config_by_namespace_id(MYSQL * mysql, list<SFS_DATA_NODE_CONFIG> & l_config, \
				int namespace_id) {
		if (NULL == mysql || namespace_id < 0) {
				LOG4CXX_ERROR(_logger, "get_data_node_config_by_namespace_id error, invalid args, namespace_id:" << \
								namespace_id);
				return -1;
		}
		char t[1024];
		snprintf(t, sizeof(t), "%d", namespace_id);
		string sql = string("select DATA_NODE_IP, DATA_NODE_PORT, DATA_NODE_NAMESPACE_ID, CREATE_TIME from "
						"SFS_DATA_NODE_CONFIG where DATA_NODE_NAMESPACE_ID = ") + t;
		MysqlHelper mysql_helper;
		int res = mysql_helper.query(mysql, sql.c_str(), sql.size());
		if (res < 0) return -2;
		MYSQL_RES * mysql_res = mysql_helper.store_result(mysql, sql.c_str());
		if (NULL == mysql_res) return -3;
		MYSQL_ROW row;
		struct tm ttm;
		while ((row = mysql_fetch_row(mysql_res)) != NULL) {
				SFS_DATA_NODE_CONFIG config;
				config.data_node_ip = row[0];
				config.data_node_port = atoi(row[1]);
				config.data_node_namespace_id = atoi(row[2]);
				strptime(row[3], "%Y-%m-%d %H:%M:%S", &ttm);
				config.create_time = mktime(&ttm);
				l_config.push_back(config);
		}
		mysql_free_result(mysql_res);
		if (l_config.size() < 1) return SFS_DBOP_NOT_FOUND;
		return 1;
}

int SFSDBOperation::add_data_node_config(MYSQL * mysql, const SFS_DATA_NODE_CONFIG config) {
		if (NULL == mysql || config.data_node_ip.size() < 1 || config.data_node_port < 1 || \
						config.data_node_namespace_id < 0 ) {
				LOG4CXX_ERROR(_logger, "add_data_node_config error, invalid args");
				return -1;
		}
		string e_data_node_ip;
		MysqlHelper mysql_helper;
		int res = mysql_helper.escape_string(mysql, e_data_node_ip, config.data_node_ip.c_str(), config.data_node_ip.size());
		if (res < 0) return -2;
		char str_data_node_port[1024];
		char str_data_node_namespace_id[1024];
		snprintf(str_data_node_port, sizeof(str_data_node_port), "%d", config.data_node_port);
		snprintf(str_data_node_namespace_id, sizeof(str_data_node_namespace_id), "%d", config.data_node_namespace_id);
		string sql = string("insert into SFS_DATA_NODE_CONFIG(DATA_NODE_IP, DATA_NODE_PORT, DATA_NODE_NAMESPACE_ID) "
						"values(\'") + e_data_node_ip + "\', " + str_data_node_port + ", " + \
						str_data_node_namespace_id + ")";
		res = mysql_helper.query(mysql, sql.c_str(), sql.size());
		if (res < 0) return -3;
		return 1;
}

int SFSDBOperation::del_data_node_config(MYSQL * mysql, const char * data_node_ip, const int data_node_port) {
		if (NULL == mysql || NULL == data_node_ip || strlen(data_node_ip) < 1 || data_node_port < 1) {
				LOG4CXX_ERROR(_logger, "del_data_node_config error, invalid args");
				return -1;
		}
		string e_data_node_ip;
		MysqlHelper mysql_helper;
		int res = mysql_helper.escape_string(mysql, e_data_node_ip, data_node_ip, strlen(data_node_ip));
		if (res < 0) return -2;
		char t[1024];
		snprintf(t, sizeof(t), "%d", data_node_port);
		string sql = string("delete from SFS_DATA_NODE_CONFIG where DATA_NODE_IP = \'") + e_data_node_ip + \
					 "\' and DATA_NODE_PORT = " + t;
		res = mysql_helper.query(mysql, sql.c_str(), sql.size());
		if (res < 0) return -3;
		int affected_rows = mysql_affected_rows(mysql);
		if (affected_rows > 0) return affected_rows;
		LOG4CXX_ERROR(_logger, "del_data_node_config error, can't found target data node, sql:" << sql);
		return SFS_DBOP_NOT_FOUND;
}

int SFSDBOperation::get_namespace_config(MYSQL * mysql, SFS_NAMESPACE_CONFIG & config, const int namespace_id) {
		if (NULL == mysql || namespace_id < 0) {
				LOG4CXX_ERROR(_logger, "get_namespace_config error, invalid args, namespace_id:" << \
								namespace_id);
				return -1;
		}
		char t[1024];
		snprintf(t, sizeof(t), "%d", namespace_id);
		string sql = string("select NAMESPACE_ID, NAMESPACE_WEIGHT, CREATE_TIME from "
						"SFS_NAMESPACE_CONFIG where NAMESPACE_ID = ") + t;
		MysqlHelper mysql_helper;
		int res = mysql_helper.query(mysql, sql.c_str(), sql.size());
		if (res < 0) return -2;
		MYSQL_RES * mysql_res = mysql_helper.store_result(mysql, sql.c_str());
		if (NULL == mysql_res) return -3;
		MYSQL_ROW row;
		bool found(false);
		struct tm ttm;
		if ((row = mysql_fetch_row(mysql_res)) != NULL) {
				config.namespace_id= atoi(row[0]);
				config.namespace_weight= atoi(row[1]);
				strptime(row[2], "%Y-%m-%d %H:%M:%S", &ttm);
				config.create_time = mktime(&ttm);
				found= true;
		}
		mysql_free_result(mysql_res);
		if (found) return 1;
		return SFS_DBOP_NOT_FOUND;
}

int SFSDBOperation::get_all_namespace_config(MYSQL * mysql, list<SFS_NAMESPACE_CONFIG> & l_config) {
		if (NULL == mysql) {
				LOG4CXX_ERROR(_logger, "get_all_namespace_config error, invalid args");
				return -1;
		}
		string sql = string("select NAMESPACE_ID, NAMESPACE_WEIGHT, CREATE_TIME from SFS_NAMESPACE_CONFIG");
		MysqlHelper mysql_helper;
		int res = mysql_helper.query(mysql, sql.c_str(), sql.size());
		if (res < 0) return -2;
		MYSQL_RES * mysql_res = mysql_helper.store_result(mysql, sql.c_str());
		if (NULL == mysql_res) return -3;
		MYSQL_ROW row;
		struct tm ttm;
		while ((row = mysql_fetch_row(mysql_res)) != NULL) {
				SFS_NAMESPACE_CONFIG config;
				config.namespace_id= atoi(row[0]);
				config.namespace_weight= atoi(row[1]);
				strptime(row[2], "%Y-%m-%d %H:%M:%S", &ttm);
				config.create_time = mktime(&ttm);
				l_config.push_back(config);
		}
		mysql_free_result(mysql_res);
		if (l_config.size() > 0) return 1;
		return SFS_DBOP_NOT_FOUND;
}
int SFSDBOperation::add_namespace_config(MYSQL * mysql, const SFS_NAMESPACE_CONFIG config) {
		
		if (NULL == mysql || config.namespace_id < 0 || config.namespace_weight < 0) {
				LOG4CXX_ERROR(_logger, "add_namespace_config error, invalid args, namespace_id:" << config.namespace_id << \
								", weight:" << config.namespace_weight);
								
				return -1;
		}
		
		char str_namespace_id[1024];
		char str_namespace_weight[1024];
		snprintf(str_namespace_id, sizeof(str_namespace_id), "%d", config.namespace_id);
		snprintf(str_namespace_weight, sizeof(str_namespace_weight), "%d", config.namespace_weight);
		MysqlHelper mysql_helper;
		string sql = string("insert into SFS_NAMESPACE_CONFIG(NAMESPACE_ID, NAMESPACE_WEIGHT) "
						"values(") + str_namespace_id + ", " + str_namespace_weight + ")";
		int res = mysql_helper.query(mysql, sql.c_str(), sql.size());
		if (res < 0) return -2;
		return 1;
}

int SFSDBOperation::del_namespace_config(MYSQL * mysql, const int namespace_id) {
		if (NULL == mysql || namespace_id < 0) {
				LOG4CXX_ERROR(_logger, "del_namespace_config error, invalid args, namespace_id:" << namespace_id);
				return -1;
		}
		char t[1024];
		snprintf(t, sizeof(t), "%d", namespace_id);
		string sql = string("delete from SFS_NAMESPACE_CONFIG where NAMESPACE_ID = ") + t;
		MysqlHelper mysql_helper;
		int res = mysql_helper.query(mysql, sql.c_str(), sql.size());
		if (res < 0) return -2;
		int affected_rows = mysql_affected_rows(mysql);
		if (affected_rows > 0) return affected_rows;
		LOG4CXX_ERROR(_logger, "del_namespace_config error, can't found target namespace_id, sql:" << sql);
		return SFS_DBOP_NOT_FOUND;

}

int SFSDBOperation::mod_namespace_config(MYSQL * mysql, const SFS_NAMESPACE_CONFIG config, \
				const set<string> need_mod_fields, int namespace_id) {
		if (NULL == mysql || namespace_id < 0 || need_mod_fields.size() < 1) {
				LOG4CXX_ERROR(_logger, "mod_namespace_config error, invalid args, namespace_id:" << namespace_id << \
								", need_mod_fields size:" << need_mod_fields.size());
				return -1;
		}
		string sql = "update SFS_NAMESPACE_CONFIG set ";
		char t[1024];
		bool first_update_flag(true);
		for(set<string>::const_iterator iter = need_mod_fields.begin(); need_mod_fields.end() != iter; iter++) {
				if(*iter == "NAMESPACE_WEIGHT") {
						if (config.namespace_weight < 0) {
								LOG4CXX_ERROR(_logger, "mod_namespace_config error, invalid args:weight, value:" << \
												config.namespace_weight);
								return -2;
						}
						if (!first_update_flag) sql += ", ";
						snprintf(t, sizeof(t), "%d", config.namespace_weight);
						sql += string("NAMESPACE_WEIGHT = ") + t;
						first_update_flag = false;
				}
		}
		if (first_update_flag) {
				LOG4CXX_ERROR(_logger, "mod_namespace_config error, specify no field to be modified");
				return -3;
		}
		snprintf(t, sizeof(t), "%d", namespace_id);
		sql += string(" where NAMESPACE_ID = ") + t;
		MysqlHelper mysql_helper;
		int res = mysql_helper.query(mysql, sql.c_str(), sql.size());
		if (res < 0) return -4;
		int affected_rows = mysql_affected_rows(mysql);
		if (affected_rows > 0) return affected_rows;
		LOG4CXX_ERROR(_logger, "mod_namespace_config error, can't found target namespace_id, sql:" << sql);
		return SFS_DBOP_NOT_FOUND;
}

int SFSDBOperation::get_file_metadata(MYSQL * mysql, SFS_FILE_METADATA & metadata, const int namespace_id, \
								const char * file_path) {
		if (NULL == mysql || namespace_id < 0 || NULL == file_path || strlen(file_path) < 1) {
				LOG4CXX_ERROR(_logger, "get_file_metadata error, invalid args");
				return -1;
		}
		string e_file_path;
		MysqlHelper mysql_helper;
		int res = mysql_helper.escape_string(mysql, e_file_path, file_path, strlen(file_path));
		if (res < 0) return -2;
		char t[1024];
		snprintf(t, sizeof(t), "SFS_NAMESPACE_%d_FILE", namespace_id);
		string sql = string("select FILE_PATH, FILE_SIZE, FILE_VERSION, LOCK_FLAG, LOCK_TIME, CREATE_TIME from ") + t + \
					 " where FILE_PATH = \'" + e_file_path + "\'";
		res = mysql_helper.query(mysql, sql.c_str(), sql.size());
		if (res < 0) return -3;
		MYSQL_RES * mysql_res = mysql_helper.store_result(mysql, sql.c_str());
		if (NULL == mysql_res) return -4;
		MYSQL_ROW row;
		struct tm ttm;
		bool found(false);
		if ((row = mysql_fetch_row(mysql_res)) != NULL) {
				metadata.file_path = row[0];
				metadata.file_size = atoi(row[1]);
				metadata.file_version = atoi(row[2]);
				metadata.lock_flag = atoi(row[3]);
				if (NULL == row[4]) metadata.lock_time = -1;
				else {
						strptime(row[4], "%Y-%m-%d %H:%M:%S", &ttm);
						metadata.lock_time = mktime(&ttm);
				}
				strptime(row[5], "%Y-%m-%d %H:%M:%S", &ttm);
				metadata.create_time = mktime(&ttm);
				found = true;
		}
		mysql_free_result(mysql_res);
		if (found) return 1;
		return SFS_DBOP_NOT_FOUND;
}

int SFSDBOperation::add_file_metadata(MYSQL * mysql, const SFS_FILE_METADATA metadata, const int namespace_id) {
		if (NULL == mysql || namespace_id < 0 || metadata.file_path.size() < 1 || metadata.file_size < 0 || \
						metadata.file_version < 0) {
				LOG4CXX_ERROR(_logger, "add_file_metadata error, invalid args");
				return -1;
		}
		string e_file_path;
		char str_table_name[1024];
		char str_file_size[1024];
		char str_file_version[1024];
		MysqlHelper mysql_helper;
		int res = mysql_helper.escape_string(mysql, e_file_path, metadata.file_path.c_str(), metadata.file_path.size());
		if (res < 0) return -2;
		snprintf(str_table_name, sizeof(str_table_name), "SFS_NAMESPACE_%d_FILE", namespace_id);
		snprintf(str_file_size, sizeof(str_file_size), "%d", metadata.file_size);
		snprintf(str_file_version, sizeof(str_file_version), "%d", metadata.file_version);
		string sql = string("insert into ") + str_table_name + " (FILE_PATH, FILE_SIZE, FILE_VERSION, LOCK_FLAG) "
				"values(\'" + e_file_path + "\', " + str_file_size + ", " + str_file_version + ", 0)";
		res = mysql_helper.query(mysql, sql.c_str(), sql.size());
		if (res < 0) return -3;
		return 1;
}

int SFSDBOperation::del_file_metadata(MYSQL * mysql, const int namespace_id, const char * file_path) {
		if (NULL == mysql || namespace_id < 0 || NULL == file_path || strlen(file_path) < 1) {
				LOG4CXX_ERROR(_logger, "del_file_metadata error, invalid args");
				return -1;
		}
		string e_file_path;
		char str_table_name[1024];
		MysqlHelper mysql_helper;
		int res = mysql_helper.escape_string(mysql, e_file_path, file_path, strlen(file_path));
		if (res < 0) return -2;
		snprintf(str_table_name, sizeof(str_table_name), "SFS_NAMESPACE_%d_FILE", namespace_id);
		string sql = string("delete from ") + str_table_name + " where FILE_PATH = \'" + e_file_path + "\'";
		res = mysql_helper.query(mysql, sql.c_str(), sql.size());
		if (res < 0) return -3;
		int affected_rows = mysql_affected_rows(mysql);
		if (affected_rows > 0) return affected_rows;
		LOG4CXX_ERROR(_logger, "del_file_metadata error, can't found target file, sql:" << sql);
		return SFS_DBOP_NOT_FOUND;
}

int SFSDBOperation::mod_file_metadata(MYSQL * mysql, const SFS_FILE_METADATA metadata, const set<string> need_mod_fields, \
				const int namespace_id, const char * file_path) {
		if (NULL == mysql || namespace_id < 0 || need_mod_fields.size() < 1) {
				LOG4CXX_ERROR(_logger, "mod_file_metadata error, invalid args, namespace_id:" << namespace_id << \
								", need_mod_fields size:" << need_mod_fields.size());
				return -1;
		}
		char str_table_name[1024];
		snprintf(str_table_name, sizeof(str_table_name), "SFS_NAMESPACE_%d_FILE", namespace_id);
		string e_file_path;
		MysqlHelper mysql_helper;
		int res = mysql_helper.escape_string(mysql, e_file_path, file_path, strlen(file_path));
		if (res < 0) return -2;
		string sql = string("update ") + str_table_name + " set ";
		char t[1024];
		bool first_update_flag(true);
		for(set<string>::const_iterator iter = need_mod_fields.begin(); need_mod_fields.end() != iter; iter++) {
				if(*iter == "FILE_SIZE") {
						if (metadata.file_size < 0) {
								LOG4CXX_ERROR(_logger, "mod_file_metadata error, invalid args:file_size, value:" << \
												metadata.file_size);
								return -3;
						}
						if (!first_update_flag) sql += ", ";
						snprintf(t, sizeof(t), "%d", metadata.file_size);
						sql += string("FILE_SIZE = ") + t;
						first_update_flag = false;
				}
				if(*iter == "FILE_VERSION") {
						if (metadata.file_version < 0) {
								LOG4CXX_ERROR(_logger, "mod_file_metadata error, invalid args:file_version, value:" << \
												metadata.file_version);
								return -4;
						}
						if (!first_update_flag) sql += ", ";
						snprintf(t, sizeof(t), "%d", metadata.file_version);
						sql += string("FILE_VERSION = ") + t;
						first_update_flag = false;
				}

		}
		if (first_update_flag) {
				LOG4CXX_ERROR(_logger, "mod_file_metadata error, specify no field to be modified");
				return -5;
		}
		snprintf(t, sizeof(t), "%d", namespace_id);
		sql += string(" where FILE_PATH = \'") + e_file_path + "\'";
		res = mysql_helper.query(mysql, sql.c_str(), sql.size());
		if (res < 0) return -6;
		int affected_rows = mysql_affected_rows(mysql);
		if (affected_rows > 0) return affected_rows;
		LOG4CXX_ERROR(_logger, "mod_file_metadata error, can't found target file, sql:" << sql);
		return SFS_DBOP_NOT_FOUND;
}

int SFSDBOperation::lock_file(MYSQL * mysql, const int namespace_id, const char * file_path) {
		if (NULL == mysql || namespace_id < 0 || NULL == file_path || strlen(file_path) < 0) {
				LOG4CXX_ERROR(_logger, "lock_file error, invalid args");
				return -1;
		}
		string e_file_path;
	   	char table_name[1024];
		MysqlHelper mysql_helper;
		int res = mysql_helper.escape_string(mysql, e_file_path, file_path, strlen(file_path));
		if (res < 0) return -2;
		snprintf(table_name, sizeof(table_name), "SFS_NAMESPACE_%d_FILE", namespace_id);
		string sql = string("update ") + table_name + " set LOCK_FLAG = 1, LOCK_TIME = current_timestamp where "
				"file_path = \'" + e_file_path + "\' and LOCK_FLAG = 0";
		res = mysql_helper.query(mysql, sql.c_str(), sql.size());
		if (res < 0) return -3;
		int affected_rows = mysql_affected_rows(mysql);
		if (affected_rows > 0) return affected_rows;
		LOG4CXX_ERROR(_logger, "lock file error, can't found target file, sql:" << sql);
		return SFS_DBOP_NOT_FOUND;
}

int SFSDBOperation::unlock_file(MYSQL * mysql, const int namespace_id, const char * file_path) {
		if (NULL == mysql || namespace_id < 0 || NULL == file_path || strlen(file_path) < 0) {
				LOG4CXX_ERROR(_logger, "unlock_file error, invalid args");
				return -1;
		}
		string e_file_path;
	   	char table_name[1024];
		MysqlHelper mysql_helper;
		int res = mysql_helper.escape_string(mysql, e_file_path, file_path, strlen(file_path));
		if (res < 0) return -2;
		snprintf(table_name, sizeof(table_name), "SFS_NAMESPACE_%d_FILE", namespace_id);
		string sql = string("update ") + table_name + " set LOCK_FLAG = 0, LOCK_TIME = NULL where "
				"file_path = \'" + e_file_path + "\' and LOCK_FLAG = 1";
		res = mysql_helper.query(mysql, sql.c_str(), sql.size());
		if (res < 0) return -3;
		int affected_rows = mysql_affected_rows(mysql);
		if (affected_rows > 0) return affected_rows;
		LOG4CXX_ERROR(_logger, "unlock file error, can't found target file, sql:" << sql);
		return SFS_DBOP_NOT_FOUND;
}
